package chatController;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import javax.swing.JOptionPane;

import cs.remoteapp.FileAnswer;
import cs.remoteapp.FileRequest;
import cs.remoteapp.Goodbye;
import cs.remoteapp.Hello;
import cs.remoteapp.TextMessage;
import chatGUI.ChatGUI;
import chatNI.ChatNI;
import people.LocalUser;
import people.User;
import people.UserType;
import model.ChatModelFacade;

/**
 * <B>Project ChatSystemG2 - INSA 4INFOA1 - 2013/2014</B>
 * <p>
 * <I>Package  chatController</I>
 * <br>
 * ChatController is the controller in the MVC design pattern. It updates the views using the model.
 * <br>
 * It implements 2 interfaces: ContToGUI and ContToNI, that represent the relations between the controller and GUI and NI.
 * <br>
 * It contains the following attributes: the chatGUI, the chatNI, the chatModelFacade and the local user.
 *
 * @author Sergio Gonzalez - Marie-Charlotte Bideaud
 */

public class ChatController implements ContToGUI, ContToNI {

	private ChatModelFacade cmf;
	private ChatNI cni;
	private ChatGUI cgui;
	
	private LocalUser luser;
	
	/**
	 * Constructor of the class ChatController
	 * @param cmf : ChatModelFacade 
	 * @param udpPort : int
	 * @param tcpPort : int
	 */
	public ChatController (ChatModelFacade cmf, int udpPort, int tcpPort){
		this.cmf = cmf;
		try {
			luser = new LocalUser("", UserType.LOCAL, udpPort, tcpPort);
		} catch (UnknownHostException e1) {
			JOptionPane.showMessageDialog(null,
				    "An error occured during the collect of the local address.",
				    "Local address collect error",JOptionPane.ERROR_MESSAGE);
			performDisconnect();
		} catch (SocketException e) {
			JOptionPane.showMessageDialog(null,
				    "An error occured during the collect of the local address.",
				    "Local address collect error",JOptionPane.ERROR_MESSAGE);
			performDisconnect();
		}
		try {
			cgui = new ChatGUI(this);
		} catch (UnknownHostException e) {
			JOptionPane.showMessageDialog(null,
				    "An error occured during the collect of the network interfaces.",
				    "Network interfaces collect error",JOptionPane.ERROR_MESSAGE);
			performDisconnect();
		}
		cgui.getWindowconnect().display();
	}
	
	// *****     INTERFACE CONTTOGUI     *****
	
	/**
	 * When a connection is initialized by the local user
	 * 
	 * @param username : String 
	 */
	public void performConnect(String username) {
		// The username is setted
		luser.setUsername(username);
		
		// Creation of the chat NI (added to the observers of the model cmf)
		try {
			cni = new ChatNI(this);
		}
		catch (IOException e) {
			JOptionPane.showMessageDialog(null,
				    "An error occured during the sockets creation.",
				    "Sockets error",JOptionPane.ERROR_MESSAGE);
			performDisconnect();
			// return;
		}
		cmf.addObserver(cni);
		cni.start();
		
		// windowcomunica is a new observer of the model cmf
		cmf.addObserver(cgui.getWindowcomunica());
		
		cmf.createHelloMessage(username);
		
		cgui.listOptionRequest();
	}
	
	/**
	 * When the local user wants to disconnect from the chat 
	 */
	public void performDisconnect() {
		cmf.createGoodbyeMessage(luser.getUsername());

		try {
			cni.closeThreads();
		} catch (InterruptedException e) {
			JOptionPane.showMessageDialog(null,
			    "An error occured during the thread closure.",
			    "Thread error",JOptionPane.ERROR_MESSAGE);
		}
		
		try {
			cni.closeSockets();
		} catch (IOException e) {
			JOptionPane.showMessageDialog(null,
				    "An error occured during the sockets closure.",
				    "Sockets error",JOptionPane.ERROR_MESSAGE);
		}
		
		try {
			cni.join();
		} catch (InterruptedException e) {
			JOptionPane.showMessageDialog(null,
				    "An error occured during the cni's thread closure.",
				    "Sockets error",JOptionPane.ERROR_MESSAGE);
		}

		// Remove the chat NI and the windowComunica from the observers, close the windowComunica and display the windowConnect
		cmf.deleteObserver(cni);
		cmf.deleteObserver(cgui.getWindowcomunica());
		cmf.removeAllUser();
		cgui.loginRequest();
	}

	/**
	 * Method to send a text message to a remote user
	 * 
	 * @param user : User
	 */
	public void performSendText(User user) {
		cmf.createTextMessage(user);
	}

	/**
	 * Method to send a file request to a remote user
	 * 
	 * @param user : User
	 */
	public void performFileRequest(User user) {
		cmf.createFileRequest(user);
	}

	/**
	 * Method to send an answer after a file request 
	 * 
	 * @param user : User
	 */
	public void performAnswer(User user) {
		cmf.createFileAnswer(user);
	}

	// *****     INTERFACE CONTTONI     *****
	
	/**
	 * What to do when a message is received, depending on its type
	 * 
	 * @param object : Object
	 * @param sourceAddress : InetAddress
	 */
	public void selectProcess(Object object, InetAddress sourceAddress) {
		if (!luser.getIPAddr().equals(sourceAddress)) {  // if the message comes from the local user, we ignore it
			if (object instanceof Hello) { // if the message is a Hello
				Hello hellomsg = (Hello) object;
				if (hellomsg.isBroadcast()) { // if it is a braodcasted hello : answer and add the user to the userslist
					cmf.createHelloReponse(luser.getUsername(), sourceAddress);
  					User user = new User(hellomsg.getUsername(), UserType.CONNECT);
  					user.setIPAddr(sourceAddress);
  					cmf.addUser(user);
				} else { // if the hello is not broadcasted (it is an answer) : add the user to the userslist
					User user = new User(hellomsg.getUsername(), UserType.CONNECT);
					user.setIPAddr(sourceAddress);
					cmf.addUser(user);
				}
			}
			if (object instanceof Goodbye) { // if the message is a goodbye : remove the user from the userslist
				Goodbye byemsg = (Goodbye) object;
				User user = new User(byemsg.getUsername(), UserType.DISCONNECT);
				user.setIPAddr(sourceAddress);
				cmf.removeUser(user);
			}
			if (object instanceof TextMessage) { // if the message is a text message 
				TextMessage textmsg = (TextMessage) object;
				User user = new User(textmsg.getUsername(), UserType.TEXT);
				user.setIPAddr(sourceAddress);
				user.setText(textmsg.getText());
				cmf.setTextMessage(user);
			}
			if (object instanceof FileRequest) { // if the message is a file request 
				FileRequest fileRequest = (FileRequest) object;
				User user = new User(fileRequest.getUsername(), UserType.FILEREQUEST);
				user.setIPAddr(sourceAddress);
				user.setFilename(fileRequest.getFileName());
				user.setFilesize(fileRequest.getFileSize());
				cmf.setFileRequest(user);
			}
			if (object instanceof FileAnswer) { // if the message is a file answer 
				FileAnswer fileAnswer = (FileAnswer) object;
				User user = new User(fileAnswer.getUsername(), UserType.FILEANSWER);
				user.setIPAddr(sourceAddress);
				cmf.setFileAnswer(user, fileAnswer.isAccepted());
				if (fileAnswer.isAccepted()) {
					cni.sendFile();
				}
			}
		}
	}
	
	/**
	 * Getter of the local user of a chat session
	 * 
	 * @return the local user
	 */
	public LocalUser getLocaluser(){
		return luser;
	}
	
	
}
